/*******************************************************************************
 * @file
 * @brief   虚拟串口
 *******************************************************************************/
#include "bsp.h"
#include "bsp_api.h"
#include "pincfg.h"
#include "dataproc.h"
#include "com_obj.h"
#include "bsp_cpu_it.h"
#include "str_ext.h"
#include "stm32h5xx_ll_usart.h"

/*******************************************************************************
 * @brief   本地变量
 *******************************************************************************/

/*******************************************************************************
 * @brief   虚拟接口
 *******************************************************************************/
#ifndef ComObj

USART_TypeDef *UartSim;
#define com_dir_RxEnable()
#define com_dir_TxEnable()

/*******************************************************************************
 * @brief   接口功能
 *******************************************************************************/
#define COM_RX_EN 1
#define COM_RX_CALL_BACK_EN 0
#define MB_RS_485_MODE_EN 0
#define MB_RS_485_HALDIR_EN 0

#define comPORT UartSim
#define comIsr COM_UART_Sim_IRQHandler
#define COM_IRQn USART1_IRQn
#define ComObj com.obj01

//#define DMA_COM_RX DMA_U01_RX
//#define DMA_COM_TX DMA_U01_TX

#define COM_ISR_GEN 1
#define COM_TX_START_GEN 1
#define COM_RX_START_GEN 1

static uint8_t com_tx_buff[2];
static uint8_t com_rx_buff[2];

#define COM_PORT_FIFO_SIZE 1

static void com_pin_cfg(void) {}
	
static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity);
	
void com_sim_Init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
	com_hal_init(comMode, ulBaudRate, eParity);
}

#endif

/*******************************************************************************
 * @brief   基本接口
 *******************************************************************************/
static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity);
static void com_rx_enable(uint8_t xEnable);
static void com_tx_enable(uint8_t xEnable);
static void com_hal_send(uint8_t dat);
static void com_rcv_st_clr_hal(void);
static void com_snd_st_clr_hal(void);

static int16_t com_hal_read(void);
static void com_timer_handle(void);
static void com_fun_init(uint8_t comMode);

#if ( defined DMA_COM_RX)
static void com_dma_rx_init(void);
static void com_dma_rx_restart(void);
#endif

#if ( defined DMA_COM_TX)
static void com_dma_tx_init(void);
static void com_dma_tx_check(void);
static void com_tx_dma_irq(void);
#endif

/*******************************************************************************
 * @brief   串口底层操作
 *******************************************************************************/
#define uart_send_data(dat) comPORT->TDR = dat
#define uart_receive_data() comPORT->RDR
#define uart_rx_not_empty_st() ((comPORT->ISR & USART_ISR_RXNE) != 0) // rx not empty
#define uart_tx_not_full_st() (0 != (comPORT->ISR & USART_ISR_TXE))	 // tx not full

#define uart_rx_dma_enable()                           \
	comPORT->CR1 |= (USART_CR1_UE | USART_CR1_RE); \
	comPORT->CR3 |= USART_CR3_DMAR														  // 开启RX接收
#define uart_rx_int_enable() comPORT->CR1 |= (USART_CR1_UE | USART_CR1_RE | USART_CR1_RXNEIE) // 开启RX中断
#define uart_rx_int_disable()                               \
	comPORT->CR1 &= ~(USART_CR1_RXNEIE | USART_CR1_RE); \
	comPORT->CR3 &= ~USART_CR3_DMAR

#define uart_rx_error_clear() // 先读 sts 再读 dt自动清除

#define uart_tx_int_enable() comPORT->CR1 |= (USART_CR1_UE | USART_CR1_TE | USART_CR1_TXEIE)
#define uart_tx_dma_enable()                           \
	comPORT->CR1 |= (USART_CR1_UE | USART_CR1_TE); \
	comPORT->CR3 |= USART_CR3_DMAT
#define uart_tx_int_disable()               \
	comPORT->CR1 &= ~(USART_CR1_TXEIE); \
	comPORT->CR3 &= ~USART_CR3_DMAT
	
#define uart_tx_comp_st() ((comPORT->ISR & USART_ISR_TC) != 0)
#define uart_tx_enable() comPORT->CR1 |= (USART_CR1_UE | USART_CR1_TE)

static void uart_rx_data_clear()
{
	volatile uint8_t u8Temp;
	while (uart_rx_not_empty_st())
	{
		u8Temp = uart_receive_data();
	}
}

com_api_make(comapi, ComObj);

/*****************************************************************************
 * @brief   com object init.
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_fun_init(uint8_t comMode)
{
	ComObj.b_auto_send = 1;
	ComObj.St.bits.run_en = 1;
	ComObj.master_wait_time_sv = 200;
	ComObj.rcv_end_keep_sv = 3;
	ComObj.rx_buff = com_rx_buff;
	ComObj.tx_buff = com_tx_buff;
	ComObj.rx_buff_size = sizeof(com_rx_buff);
	ComObj.tx_buff_size = sizeof(com_tx_buff);
	ComObj.St.bits.ov_mode = 1; // 保证接收最新数据

	ComObj.fun = com_api_name(comapi);

	ComObj.b_master_mode = comMode;
}

/*****************************************************************************
 * @brief   	com modle init .
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_hal_init(uint8_t comMode, uint32_t ulBaudRate, com_parity_type eParity)
{
	uint32_t us10TimerSv;
	uint32_t ClocksPer2ms;
	LL_USART_InitTypeDef cfg;

// 关闭 DMA
#if ( defined DMA_COM_RX)
	dma_nvic_set(&DMA_COM_RX, 0, 0);
	dma_stream_disable(&DMA_COM_RX);	
	dma_stream_deinit(&DMA_COM_RX);
#endif

#if ( defined DMA_COM_TX)
	dma_nvic_set(&DMA_COM_TX, 0, 0);
	dma_stream_disable(&DMA_COM_TX);	
	dma_stream_deinit(&DMA_COM_TX);
#endif

	// 模块初始化
	com_pin_cfg();

	set_u8(&ComObj, 0, sizeof(ComObj));

	cfg.BaudRate = ulBaudRate;
	cfg.TransferDirection = LL_USART_DIRECTION_TX_RX;
	cfg.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
	cfg.OverSampling = LL_USART_OVERSAMPLING_16;

	if (eParity == COM_PAR_ODD)
	{
		cfg.Parity = LL_USART_PARITY_ODD;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}
	else if (eParity == COM_PAR_EVEN)
	{
		cfg.Parity = LL_USART_PARITY_EVEN;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_9B;
	}
	else if (eParity == COM_PAR_NONE_2S)
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_2;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}
	else
	{
		cfg.Parity = LL_USART_PARITY_NONE;
		cfg.StopBits = LL_USART_STOPBITS_1;
		cfg.DataWidth = LL_USART_DATAWIDTH_8B;
	}

	// 11*3.5*(1E6)/ba/50 = 77*1E4/ba
	if (ulBaudRate > 19200)
	{
		ClocksPer2ms = 1800 * ulBaudRate / 1000000 + 1;

		if (ClocksPer2ms < 41)
			ClocksPer2ms = 41;
		else if (ClocksPer2ms > 255)
			ClocksPer2ms = 255;
		// TOI 时间设置
	}
	else
	{
		// Time-out Register ,TX-DLY=0, TX-TIMEOUT=40
		// TOI 时间设置
	}

	// 报文通知延时
	us10TimerSv = com_timer_cal(ulBaudRate, 11 * 5, 300, 20000);
	ComObj.rcv_end_time_sv = us10TimerSv / 100;

	if (comMode > 0)
	{
		ComObj.rcv_end_time_sv++;
	}

	LL_USART_Init(comPORT, &cfg);

	LL_USART_Enable(comPORT);

	#if !(defined DMA_COM_TX) || (COM_RX_EN && !(defined DMA_COM_RX))
	nvic_irq_set(COM_IRQn, 0x06, 1);
	#endif

	com_fun_init(comMode);

#if ( defined DMA_COM_RX)
	com_dma_rx_init();
#endif

#if ( defined DMA_COM_TX)
	com_dma_tx_init();	
#endif
	
	#if COM_RX_EN > 0
	com_rx_enable(ENABLE);
	#else
	com_rx_enable(DISABLE);
	#endif
	
	uart_tx_enable();
}

#if ( defined DMA_COM_RX)
/*****************************************************************************
 * @brief   dma 接收初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_init(void)
{
	dma_stream_cfg_type cfg;

	// 配置参数
	cfg.cycle_mode = 1;
	cfg.mem_to_periph = 0;
	cfg.prio = LL_DMA_PRIORITY_MEDIUM;
	
	cfg.mem = com_rx_buff;
	cfg.periph = &comPORT->DR;
	cfg.data_len = sizeof(com_rx_buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;
	
	// 配置
	dma_stream_config(&DMA_COM_RX, &cfg);

	dma_stream_enable(&DMA_COM_RX);
}

/*****************************************************************************
 * @brief   dma 接收重置
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_rx_restart(void)
{
	// 关闭接收
	com_rx_enable(DISABLE);
	
	// 重置DMA计数器
	dma_stream_disable(&DMA_COM_RX);
	dma_stream_set_mem_addr(&DMA_COM_RX, (uu32)com_rx_buff);
	dma_stream_set_data_len(&DMA_COM_RX, sizeof(com_rx_buff));
	dma_stream_enable(&DMA_COM_RX);
	
	// 开启接收
	
	com_rx_enable(ENABLE);
}
#endif

#if ( defined DMA_COM_TX)
/*****************************************************************************
 * @brief   dma 发送初始化
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_init(void)
{
	dma_stream_cfg_type cfg;
	
	// 配置参数
	cfg.cycle_mode = 0;
	cfg.mem_to_periph = 1;
	cfg.prio = LL_DMA_PRIORITY_MEDIUM;
	
	cfg.mem = com_tx_buff;
	cfg.periph = &comPORT->DR;
	cfg.data_len = sizeof(com_tx_buff);
	cfg.mem_inc = EXT_TRUE;
	cfg.periph_inc = EXT_FALSE;
	
	// 配置
	dma_stream_config(&DMA_COM_TX, &cfg);
	dma_stream_complete_isr_enable(&DMA_COM_TX);
	dma_nvic_set(&DMA_COM_TX, 1, com_tx_dma_irq);

	// DMA 模式不自动处理发送
	ComObj.b_auto_send = 0;
}

/*****************************************************************************
 * @brief   dma 发送中断
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_tx_dma_irq(void)
{
	uint16_t index_send;

	// 清除标志 并关闭通道
	dma_comp_flag_clr(&DMA_COM_TX);
	dma_stream_disable(&DMA_COM_TX);

	// 发送索引移动
	index_send = ComObj.index_tx_send;
	index_send += ComObj.hal_ctr.dma_send_amount;
	if (index_send >= ComObj.tx_buff_size)
		index_send -= ComObj.tx_buff_size;
	ComObj.index_tx_send = index_send;
	
	// 发送状态复位
	ComObj.hal_ctr.dma_send_amount = 0;

	// 检测是否继续发送
	com_dma_tx_check();

	// 发送结束
	if (!is_dma_stream_enable(&DMA_COM_TX))
	{
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
		ComObj.hal_ctr.st_dir_ctr_req = 1;
#else
		ComObj.snd_st = COM_TX_IDLE; // 发送结束
		com_tx_enable(DISABLE);
#endif
	}
}

/*****************************************************************************
 * @brief   dma tx check: 该函数需要重入，所有操作在临界段内执行
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dma_tx_check(void)
{
	intx_alloc();

	intx_disable();

	// 发送监测
	if (!is_dma_stream_enable(&DMA_COM_TX))
	{
		if(ComObj.index_tx_fill != ComObj.index_tx_send)
		{
			uu16 amount;
			
			// 发送状态
			ComObj.hal_ctr.st_dir_ctr_req = 0;
			com_tx_enable(ENABLE);

			// DMA 发送数量计算
			if (ComObj.index_tx_fill > ComObj.index_tx_send)
				amount = ComObj.index_tx_fill - ComObj.index_tx_send;
			else
				amount = ComObj.tx_buff_size - ComObj.index_tx_send;

			// 开启DMA传输
			dma_stream_set_mem_addr(&DMA_COM_TX, (uu32)&com_tx_buff[ComObj.index_tx_send]);
			dma_stream_set_data_len(&DMA_COM_TX, amount);
			dma_stream_enable(&DMA_COM_TX);
			
			ComObj.hal_ctr.dma_send_amount = amount;
			ComObj.hal_ctr.timeout_cnt = 0;
			ComObj.hal_ctr.timeout_set = 200;
		}
	}
	else
	{
		ComObj.hal_ctr.timeout_cnt ++;
		if(ComObj.hal_ctr.timeout_cnt >= ComObj.hal_ctr.timeout_set)
		{
			// DMA 超时
			
		}
	}
	intx_enable();
}
#endif

/*****************************************************************************
 * @brief   com 方向处理.
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_dir_handle()
{
	if (ComObj.hal_ctr.st_dir_ctr_req)
	{
#if ((MB_RS_485_MODE_EN > 0) && (MB_RS_485_HALDIR_EN == 0))
		// RS485模式且软件控制方向时，发送结束切换为接收模式
		if (uart_tx_comp_st())
		{
			ComObj.hal_ctr.st_dir_ctr_req = 0;
			ComObj.snd_st = COM_TX_IDLE; // 发送结束
			com_tx_enable(DISABLE);
			if (ComObj.b_master_mode) // 主机进入接收等待状态
			{
				ComObj.rcv_st = COM_RX_WAIT;
				com_rx_enable(ENABLE);
			}
		}
#endif
	}
}

/*****************************************************************************
 * @brief   com end timer.
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_timer_handle(void)
{
#if ( defined DMA_COM_RX)
	uint16_t rx_site;
#endif
	
	// RS485方向控制
	com_dir_handle();

	// DMA 发送检测
#if ( defined DMA_COM_TX)
	com_dma_tx_check();
#endif

#if ( defined DMA_COM_RX)
	// DMA 倒计数。循环模式 计数值1再次递减变为初始数量。
	rx_site = dma_stream_get_data_len(&DMA_COM_RX);
	rx_site = ComObj.rx_buff_size - rx_site;
	if(rx_site >= ComObj.rx_buff_size)
		rx_site = 0;
	if(rx_site != ComObj.index_rx_rcv)
	{
		ComObj.St.bits.port_st = 1;
		ComObj.rcv_st = COM_RX_RCV; // 指示正在接收数据
		ComObj.b_timer_on = 1;
		ComObj.rcv_end_time_cnt = 0;
		
		ComObj.index_rx_rcv = rx_site;
	}
#endif
	
	// 运行计时
	com_timer_run(&ComObj);	
}

/*****************************************************************************
 * @brief   循环模式中断处理
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
#if COM_ISR_GEN > 0
static void comIsr(void)
{
	uint32_t IntSt, RegCR;
	
	IntSt = comPORT->ISR;
	RegCR = comPORT->CR1;
	
	ComObj.master_wait_time_cnt = 0;

#if COM_RX_EN > 0
	if ((IntSt & USART_ISR_RXNE) && (RegCR & USART_CR1_RXNEIE))
	{
		uint16_t ReadCnt;

		// FIFO 中断
		ComObj.St.bits.port_st = 1;

		// 接收状态
		ComObj.rcv_st = COM_RX_RCV; // 指示正在接收数据
		ComObj.b_timer_on = 1;
		ComObj.rcv_end_time_cnt = 0;

		// 读数据
		ReadCnt = COM_PORT_FIFO_SIZE; // FIFO read
		while (ReadCnt--)
		{
			uint8_t datr;
			datr = uart_receive_data();

			com_rx_rcv(&ComObj, datr);

#if COM_RX_CALL_BACK_EN > 0
			if (ComObj.rx_call_back)
			{
				ComObj.rx_call_back(datr);
			}
#endif
		}
	}
	else if ((IntSt & USART_ISR_TXE) && (RegCR & USART_CR1_TXEIE))
#else
	if ((IntSt & USART_SR_TXE) && (RegCR & USART_CR1_TXEIE))
#endif
	// 发送缓冲空触发
	{
		if (ComObj.index_tx_send == ComObj.index_tx_fill)
		{
			// 发送已结束 //关闭中断
			com_tx_enable(0);
			if (ComObj.b_master_mode)
			{
				ComObj.rcv_st = COM_RX_WAIT;
			}
		}
		else
		{
			// 发送空中断
			while (uart_tx_not_full_st()) // 缓冲未满
			{
				if (ComObj.index_tx_send != ComObj.index_tx_fill) // 写缓冲区结束
				{
					// 继续发送
					uart_send_data(ComObj.tx_buff[ComObj.index_tx_send++]);

					if (ComObj.index_tx_send >= ComObj.tx_buff_size)
					{
						ComObj.index_tx_send = 0;
					}
				}
				else
				{
					// 发送结束
					break;
				}
			}
		}
	}
	else
	{
		// 未知错误，应重新初始化串口设备
		//  ComObj.St.bits.err_hal = 1;
		//  nvic_irq_set(COM_IRQn,0x0F,0);
	}
}
#endif

/*****************************************************************************
 * @brief   Uart Rx En or Dis.
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
#if COM_RX_START_GEN > 0
static void com_rx_enable(uint8_t xRxEnable)
{
	volatile uint8_t u8Temp;

	if (xRxEnable)
	{
#if COM_RX_EN > 0
		// 清空接收缓冲
		uart_rx_data_clear();

		// 清除帧错误和奇偶校验错误
		uart_rx_error_clear();

		// 开始接收
#if ( defined DMA_COM_RX)
		uart_rx_dma_enable();
#else
		uart_rx_int_enable();
#endif

		com_dir_RxEnable();
#else
		uart_rx_int_disable();
#endif
	}
	else
	{
		// 关闭接收
		uart_rx_int_disable();
	}
}
#endif

/*****************************************************************************
 * @brief   Uart Tx En or Dis.
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
#if COM_TX_START_GEN > 0
static void com_tx_enable(uint8_t xEnable)
{
	if (xEnable)
	{
		// 准备发送
		com_dir_TxEnable();

		ComObj.snd_st = COM_TX_XMIT;

		// 发送中断 或 DMA开启
#if ( defined DMA_COM_TX)
		uart_tx_dma_enable();
#else
		uart_tx_int_enable();
#endif
	}
	else
	{
		// 关闭发送
		uart_tx_int_disable();
		com_dir_RxEnable();
		ComObj.snd_st = COM_TX_IDLE;
	}
}
#endif

/*****************************************************************************
 * @brief   直接发送
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_hal_send(uint8_t dat)
{
	uart_tx_enable();

	uart_send_data(dat);

	while (!uart_tx_not_full_st())
	{
	}
}

/*****************************************************************************
 * @brief   直接读取
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static int16_t com_hal_read(void)
{
	int16_t dat;

	if (comPORT->ISR & USART_ISR_RXNE)
	{
		dat = uart_receive_data();
	}
	else
	{
		dat = -1;
	}
	return dat;
}

/*****************************************************************************
 * @brief   调用接收状态初始化接口时硬件回调
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_rcv_st_clr_hal(void)
{
#if ( defined DMA_COM_RX)
	com_dma_rx_restart();
#endif
}

/*****************************************************************************
 * @brief   调用发送状态初始化接口时硬件回调
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void com_snd_st_clr_hal(void)
{

}
